/****************************************************************************
 *
 * MODULE:             JenNet-IP Border Router
 *
 * COMPONENT:          Buffered, interrupt driven serial I/O
 *
 * VERSION:            $Name:  $
 *
 * REVISION:           $Revision: 1.2 $
 *
 * DATED:              $Date: 2009/01/22 14:02:35 $
 *
 * STATUS:             $State: Exp $
 *
 * AUTHOR:             Lee Mitchell
 *
 * DESCRIPTION:
 * Just some simple common uart functions
 *
 * LAST MODIFIED BY:   $Author: lmitch $
 *                     $Modtime: $
 *
 ****************************************************************************
 *
 * This software is owned by NXP B.V. and/or its supplier and is protected
 * under applicable copyright laws. All rights are reserved. We grant You,
 * and any third parties, a license to use this software solely and
 * exclusively on NXP products [NXP Microcontrollers such as JN5148, JN5142, JN5139].
 * You, and any third parties must reproduce the copyright and warranty notice
 * and any other legend of ownership on each copy or partial copy of the
 * software.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright NXP B.V. 2012. All rights reserved
 *
 ***************************************************************************/

/****************************************************************************/
/***        Include files                                                 ***/
/****************************************************************************/
#include <jendefs.h>
#include "contiki-conf.h"
#include "sys/rtimer.h"
#include <math.h>
#include <AppHardwareApi.h>
#include <dbg.h>
#include "uart-driver.h"

#define UART_NUM_UARTS  2

#if UART_XONXOFF_FLOW_CTRL
#define UART_USE_TX_IRQ 0 /* TX buffering is not working well yet */
#include "sys/process.h"
#define TX_FIFO_SW_FLOW_LIMIT 8 /* Maximum allowed fill level for tx fifo */

#if TX_FIFO_SW_FLOW_LIMIT > 16
#undef TX_FIFO_SW_FLOW_LIMIT
#define TX_FIFO_SW_FLOW_LIMIT 16
#warning "TX_FIFO_SW_FLOW_LIMIT too big. Forced to 16."
#endif /* TX_FIFO_SW_FLOW_LIMIT > 16 */

#define XON   17
#define XOFF  19
extern volatile unsigned char xonxoff_state;
#if UART_USE_TX_IRQ
#include "ringbuf32.h"
/* PROCESS(uart_tx_process, "UART: TX process"); */
/* A ringbuf for tx when remote stops accepting data [SW flowctrl] */
#define TX_FIFO_SIZE 16
static uint8_t tx_fifo_data[TX_FIFO_SIZE];
static struct ringbuf32 tx_queue[UART_NUM_UARTS];
static void uart_driver_set_tx_interrupt(uint8_t uart_dev, uint8_t e);
static void uart_driver_tx_handler(uint8_t uart_dev);
#endif /* UART_USE_TX_IRQ */
#endif /* UART_XONXOFF_FLOW_CTRL */

/***        Macro Definitions                                             ***/
#define BUSYWAIT_UNTIL(cond, max_time)                                  \
  do {                                                                  \
    rtimer_clock_t t0;                                                  \
    t0 = RTIMER_NOW();                                                  \
    while(!(cond) && RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + (max_time)));   \
  } while(0)

#define DEBUG_UART_BUFFERED FALSE

#define CHAR_DEADLINE (uart_char_delay * 100)

/***        Local Function Prototypes                                     ***/
static void uart_driver_isr(uint32_t device_id, uint32_t item_bitmap);
static int16_t uart_driver_get_tx_fifo_available_space(uint8_t uart_dev);
static void uart_driver_set_baudrate(uint8_t uart_dev, uint8_t br);
static void uart_driver_set_high_baudrate(uint8_t uart_dev, uint32_t baud_rate);

/***        Local Variables                                               ***/
static uint16_t tx_fifo_size[UART_NUM_UARTS] = { 0 };
static uint8_t active_uarts[UART_NUM_UARTS] = { 0 };
/** slip input function pointer */
static int (*uart_input[UART_NUM_UARTS])(unsigned char) = { 0 };
/* time in uSec for transmitting 1 char */
static uint16_t uart_char_delay = 0;
static volatile int8_t interrupt_enabled[UART_NUM_UARTS] = { 0 };
static volatile int8_t interrupt_enabled_saved[UART_NUM_UARTS] = { 0 };

/***        Exported Functions                                            ***/

/****************************************************************************
 *
 * NAME:       uart_driver_init
 *
 * DESCRIPTION:
 * Initializes the specified UART device.
 *
 * PARAMETERS:      Name             RW  Usage
 *                  uart_dev         R   UART to initialise, eg, E_AHI_UART_0
 *                  br               R   Baudrate to use (e.g. UART_RATE_115200)
 *                                       if br > UART_RATE_115200
 *                                       then uart_driver_set_baud_rate is called
 *                                       else vAHI_UartSetClockDivisor
 *                  txbuf_data       R   Pointer to a memory block to use
 *                  and rxbuf_data       as uart tx/rx fifo
 *                  txbuf_size       R   size of tx fifo (valid range: 16-2047)
 *                  txbuf_size       R   size of rx fifo (valid range: 16-2047)
 *                  uart_input_function  a function pointer to input uart rx bytes
 * RETURNS:
 * void
 *
 ****************************************************************************/
void
uart_driver_init(uint8_t uart_dev, uint8_t br, uint8_t *txbuf_data,
    uint16_t txbuf_size, uint8_t *rxbuf_data, uint16_t rxbuf_size,
    int (*uart_input_function)(unsigned char c))
{
#if !UART_HW_FLOW_CTRL
  /* Disable RTS/CTS */
  vAHI_UartSetRTSCTS(uart_dev, FALSE);
#endif

#if UART_XONXOFF_FLOW_CTRL && UART_USE_TX_IRQ
  ringbuf32_init(&tx_queue[uart_dev], txbuf_data, txbuf_size);
  txbuf_data = tx_fifo_data;
  txbuf_size = TX_FIFO_SIZE;
#endif /* UART_XONXOFF_FLOW_CTRL */

  tx_fifo_size[uart_dev] = txbuf_size;

  /* Configure the selected Uart */
  uint8_t uart_enabled = bAHI_UartEnable(uart_dev, txbuf_data, txbuf_size,
      rxbuf_data, rxbuf_size);
  /* fallback to internal buffers */
  if(!uart_enabled) {
    vAHI_UartEnable(uart_dev);
    tx_fifo_size[uart_dev] = 16; /* Fixed size */
  }
  /* Reset tx/rx fifos */
  vAHI_UartReset(uart_dev, TRUE, TRUE);
  vAHI_UartReset(uart_dev, FALSE, FALSE);

  uart_driver_set_baudrate(uart_dev, br);

  /* install interrupt service callback */
  if(uart_dev == E_AHI_UART_0) {
    vAHI_Uart0RegisterCallback((void*) uart_driver_isr);
  } else {
    vAHI_Uart1RegisterCallback((void*) uart_driver_isr);
  }

  /* Enable RX interrupt */
  uart_driver_enable_interrupts(uart_dev);
  uart_input[uart_dev] = uart_input_function;
  active_uarts[uart_dev] = 1;

#if UART_HW_FLOW_CTRL
  /* Configure HW flow control */
  vAHI_UartSetAutoFlowCtrl(uart_dev, E_AHI_UART_FIFO_ARTS_LEVEL_13, /* uint8 const u8RxFifoLevel,*/
      FALSE, /* bool_t const bFlowCtrlPolarity,*/
      TRUE, /* bool_t const bAutoRts, */
      TRUE /* bool_t const bAutoCts */);
#endif

#if UART_XONXOFF_FLOW_CTRL && UART_USE_TX_IRQ
//  uart_driver_set_tx_interrupt(uart_dev, TRUE);
#endif /* UART_XONXOFF_FLOW_CTRL */

  printf("UART %d init: using %s buffers %d\n", uart_dev,
      uart_enabled ? "external" : "internal", tx_fifo_size[uart_dev]);
}

void
uart_driver_enable_interrupts(uint8_t uart_dev)
{
  /* wait while char being tx is done */
  while((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_THRE) == 0);

  vAHI_UartSetInterrupt(uart_dev, FALSE /*bEnableModemStatus*/,
  FALSE /*bEnableRxLineStatus == Break condition */,
      FALSE /*bEnableTxFifoEmpty*/,
      TRUE /* bEnableRxData */, E_AHI_UART_FIFO_LEVEL_14);
  interrupt_enabled[uart_dev] = 1;
}

void
uart_driver_disable_interrupts(uint8_t uart_dev)
{
  /* wait while char being tx is done */
  while((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_THRE) == 0);

  vAHI_UartSetInterrupt(uart_dev, FALSE /*bEnableModemStatus*/,
  FALSE /*bEnableRxLineStatus == Break condition */,
      FALSE /*bEnableTxFifoEmpty*/,
      FALSE /* bEnableRxData */, E_AHI_UART_FIFO_LEVEL_14);
  interrupt_enabled[uart_dev] = 0;
}

void
uart_driver_store_interrupts(uint8_t uart_dev)
{
  interrupt_enabled_saved[uart_dev] = interrupt_enabled[uart_dev];
}

void
uart_driver_restore_interrupts(uint8_t uart_dev)
{
  if(interrupt_enabled_saved[uart_dev]) {
    uart_driver_enable_interrupts(uart_dev);
  } else {
    uart_driver_disable_interrupts(uart_dev);
  }
}

int8_t
uart_driver_interrupt_is_enabled(uint8_t uart_dev)
{
  return interrupt_enabled[uart_dev];
}

void
uart_driver_set_input(uint8_t uart_dev, int
(*uart_input_function)(unsigned char c))
{
  uart_input[uart_dev] = uart_input_function;
}


/****************************************************************************
 *
 * NAME:       uart_driver_read
 *
 * DESCRIPTION:
 * Reads 1 byte from the RX buffer. If there is no data in the
 * buffer, then return FALSE
 *
 * PARAMETERS:      Name            RW  Usage
 *                  uart_dev		  	R   UART to use, eg, E_AHI_UART_0
 *
 * RETURNS:
 * TRUE if a byte has been read from the queue
 *
 ****************************************************************************/
uint8_t
uart_driver_read(uint8_t uart_dev, uint8_t *data)
{
  if(data && u16AHI_UartReadRxFifoLevel(uart_dev) > 0) {
    *data = u8AHI_UartReadData(uart_dev);
    return TRUE;
  }
  return FALSE;
}

#if UART_XONXOFF_FLOW_CTRL && UART_USE_TX_IRQ
/* TX process */
/*
PROCESS_THREAD(uart_tx_process, ev, data)
{
  PROCESS_BEGIN();
  while(1) {
    PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
    uint8_t dev;
    uint8_t c;
    for(dev = E_AHI_UART_0; dev < UART_NUM_UARTS; dev++) {
      if(active_uarts[dev]) {
        while(!bQueue_IsEmpty(&tx_queue[dev])
            && u16AHI_UartReadTxFifoLevel(dev) <= TX_FIFO_SW_FLOW_LIMIT) {
          if(bQueue_Read(&tx_queue[dev], &c)) {
            uart_driver_write_with_deadline(dev, c);
          }
        }
      }
    }
  }
  PROCESS_END();
}
*/
static void
uart_driver_set_tx_interrupt(uint8_t uart_dev, uint8_t e)
{
  vAHI_UartSetInterrupt(uart_dev,
      FALSE /*bEnableModemStatus*/,
      FALSE /*bEnableRxLineStatus == Break condition */,
      e /*bEnableTxFifoEmpty*/,
      interrupt_enabled[uart_dev] /* bEnableRxData */, E_AHI_UART_FIFO_LEVEL_14);
}
static void
uart_driver_tx_handler(uint8_t uart_dev)
{
  char c;
  int counter = TX_FIFO_SW_FLOW_LIMIT > u16AHI_UartReadTxFifoLevel(uart_dev) ? TX_FIFO_SW_FLOW_LIMIT - u16AHI_UartReadTxFifoLevel(uart_dev) : 1;
  while(ringbuf32_elements(&tx_queue[uart_dev])
      && u16AHI_UartReadTxFifoLevel(uart_dev) <= TX_FIFO_SW_FLOW_LIMIT
      && xonxoff_state == XON
      && counter) {
    if((c = ringbuf32_get(&tx_queue[uart_dev])) != -1) {
      vAHI_UartWriteData(uart_dev, c);
      counter--;
    }
  }
  /* disable TX FIFO empty interrupt */
  uart_driver_set_tx_interrupt(uart_dev, FALSE);
}
void
uart_driver_write_buffered(uint8_t uart_dev, uint8_t ch)
{
  int put = 0;
  uint8_t c;
  /* Put the outgoing byte on the transmission buffer. If the buffer
     is full, we just keep on trying to put the byte into the buffer
     until it is possible to put it there. */
  do {
    put = ringbuf32_put(&tx_queue[uart_dev], ch);
    if(u16AHI_UartReadTxFifoLevel(uart_dev) <= TX_FIFO_SW_FLOW_LIMIT
      && xonxoff_state == XON) {
      if((c = ringbuf32_get(&tx_queue[uart_dev])) != -1) {
        uart_driver_write_with_deadline(uart_dev, c);
      }
    } else {
      watchdog_periodic();
    }
  } while(!put);
  if(xonxoff_state == XON && ringbuf32_elements(&tx_queue[uart_dev])) {
    /* Enable TX interrupt since we got data */
    uart_driver_set_tx_interrupt(uart_dev, TRUE);
  }
}
#else
void
uart_driver_write_buffered(uint8_t uart_dev, uint8_t ch)
{
  uart_driver_write_with_deadline(uart_dev, ch);
}
#endif /* UART_XONXOFF_FLOW_CTRL */

/****************************************************************************
 *
 * NAME:       uart_driver_write_with_deadline
 *
 * DESCRIPTION:
 * Writes one byte to the specified uart for transmission
 *
 * PARAMETERS:      Name            RW  Usage
 *                  uart_dev        R   UART to use, eg, E_AHI_UART_0
 *                  ch              R   data to transmit
 *
 * RETURNS:
 * void
 *
 ****************************************************************************/
void
uart_driver_write_with_deadline(uint8_t uart_dev, uint8_t ch)
{
#if UART_XONXOFF_FLOW_CTRL
  /* Block until host can receive data */
  /* Wait until there are less than N characters in TX FIFO */
  while(xonxoff_state != XON
      || u16AHI_UartReadTxFifoLevel(uart_dev) > TX_FIFO_SW_FLOW_LIMIT) {
    watchdog_periodic();
  }
  /* write to TX FIFO and return immediately */
  vAHI_UartWriteData(uart_dev, ch);
#else /* UART_XONXOFF_FLOW_CTRL */
  volatile int16_t write = 0;
  watchdog_periodic();
  /* wait until there is space in tx fifo */
  BUSYWAIT_UNTIL(write = (uart_driver_get_tx_fifo_available_space(uart_dev) > 0),
      CHAR_DEADLINE);
  /* write only if there is space so we do not get stuck */
  if(write) {
    /* write to TX FIFO and return immediately */
    vAHI_UartWriteData(uart_dev, ch);
  }
#endif  /* UART_XONXOFF_FLOW_CTRL */
}

void
uart_driver_write_direct(uint8_t uart_dev, uint8_t ch)
{
  /* Write character */
  vAHI_UartWriteData(uart_dev, ch);
  /* Wait for buffers to empty */
  while((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_THRE) == 0);
  while((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_TEMT) == 0);
}

/****************************************************************************
 *
 * NAME:       uart_driver_rx_handler
 *
 * DESCRIPTION:
 * Interrupt service callback for UART data reception. Reads a received
 * byte from the UART and writes it to the reception buffer if it is not
 * full.
 *
 * PARAMETERS:      Name            RW  Usage
 *                  uart_dev        R   Uart to read from
 *
 * RETURNS:
 * void
 *
 ****************************************************************************/
void
uart_driver_rx_handler(uint8_t uart_dev)
{
  /* optimization for high throughput: Read upto 32 bytes from RX fifo.
   * Disabled because it does not work with current slip_input_byte */

  /* Status from uart_input:
   *  0 means do not exit power saving mode
   * -1 means RX buffer overflow ==> stop reading
   *  1 means end of slip packet
   */
#if UART_XONXOFF_FLOW_CTRL
  /* save old status */
  int xonxoff_state_old = xonxoff_state;
#endif /* UART_XONXOFF_FLOW_CTRL */
  int status = 0;
  int c = 0;
  while(u16AHI_UartReadRxFifoLevel(uart_dev) > 0 && c++ < 32 && status == 0) {
    if(uart_input[uart_dev] != NULL) { /* read one char at a time */

      /* process received character */
      status = (uart_input[uart_dev])(u8AHI_UartReadData(uart_dev));

  #if UART_XONXOFF_FLOW_CTRL
      /* Process XON-XOFF*/
      if(xonxoff_state == XOFF) {
        /* XXX do not set break condition as it corrupts one character, instead we block on TX */
        /* Instruct uart to stop TX */
        /* vAHI_UartSetBreak(uart_dev, TRUE); */
  #if UART_USE_TX_IRQ
//        uart_driver_set_tx_interrupt(uart_dev, FALSE);
  #endif /* UART_USE_TX_IRQ */
        break;
      } else if(xonxoff_state_old == XOFF && xonxoff_state == XON) {
        /* Instruct uart to resume TX if it was stopped */
        /* vAHI_UartSetBreak(uart_dev, FALSE); */
  #if UART_USE_TX_IRQ
        uart_driver_set_tx_interrupt(uart_dev, TRUE);
  #endif /* UART_USE_TX_IRQ */
      }
  #endif /* UART_XONXOFF_FLOW_CTRL */

    } else {
      /* no input handler, or no bytes to read: Discard byte. */
      u8AHI_UartReadData(uart_dev);
    }
  }
}

/****************************************************************************/
/***        Local Functions                                               ***/
/****************************************************************************/

/* Returns the free space in tx fifo, i.e., how many characters we can put */
static int16_t
uart_driver_get_tx_fifo_available_space(uint8_t uart_dev)
{
  return tx_fifo_size[uart_dev] - u16AHI_UartReadTxFifoLevel(uart_dev);
}

/* Initializes the specified UART with auto-selection of
   baudrate tuning method */
static void
uart_driver_set_baudrate(uint8_t uart_dev, uint8_t br)
{
  uint32_t high_br = 0;
  uint8_t low_br = 0;

  switch(br) {
  case UART_RATE_4800:
    low_br = E_AHI_UART_RATE_4800;
    uart_char_delay = 1667;
    break;
  case UART_RATE_9600:
    low_br = E_AHI_UART_RATE_9600;
    uart_char_delay = 834;
    break;
  case UART_RATE_19200:
    low_br = E_AHI_UART_RATE_19200;
    uart_char_delay = 417;
    break;
  case UART_RATE_38400:
    low_br = E_AHI_UART_RATE_38400;
    uart_char_delay = 209;
    break;
  case UART_RATE_76800:
    low_br = E_AHI_UART_RATE_76800;
    uart_char_delay = 105;
    break;
  case UART_RATE_115200:
    low_br = E_AHI_UART_RATE_115200;
    uart_char_delay = 69;
    break;
  case UART_RATE_230400:
    high_br = 230400UL;
    uart_char_delay = 35;
    break;
  case UART_RATE_460800:
    high_br = 460800UL;
    uart_char_delay = 18;
    break;
  case UART_RATE_500000:
    high_br = 500000UL;
    uart_char_delay = 16;
    break;
  case UART_RATE_576000:
    high_br = 576000UL;
    uart_char_delay = 14;
    break;
  case UART_RATE_921600:
    high_br = 921600UL;
    uart_char_delay = 9;
    break;
  case UART_RATE_1000000:
    high_br = 1000000UL;
    uart_char_delay = 8;
    break;
  default:
    high_br = 1000000UL;
    uart_char_delay = 8;
    break;
  }
  if(high_br == 0) {
    vAHI_UartSetClockDivisor(uart_dev, low_br);
  } else {
    uart_driver_set_high_baudrate(uart_dev, high_br);
  }
}

/****************************************************************************
 *
 * NAME:       uart_driver_set_high_baudrate
 *
 * DESCRIPTION:
 * Sets the baud rate for the specified uart
 *
 * PARAMETERS:      Name            RW  Usage
 *                  uart_dev        R   UART to initialise, eg, E_AHI_UART_0
 *                  baud_rate       R   Baudrate to use (bps eg 921600)
 *
 * RETURNS:
 * void
 *
 ****************************************************************************/
static void
uart_driver_set_high_baudrate(uint8_t uart_dev, uint32_t baud_rate)
{
  uint16 u16Divisor = 1;
  uint32_t u32Remainder;
  uint8_t u8ClocksPerBit = 16;

#if (ENABLE_ADVANCED_BAUD_SELECTION)
  /* Defining ENABLE_ADVANCED_BAUD_SELECTION in the Makefile
   * enables this code which searches for a clocks per bit setting
   * that gets closest to the configured rate.
   */
  uint32_t u32CalcBaudRate = 0;
  int32 i32BaudError = 0x7FFFFFFF;

  DBG_vPrintf(DEBUG_UART_BUFFERED, "Config uart=%d, baud=%d\n", uart_dev,
      baud_rate);

  while(abs(i32BaudError) > (int32) (baud_rate >> 4)) /* 6.25% (100/16) error */
  {
    if(--u8ClocksPerBit < 3) {
      DBG_vPrintf(DEBUG_UART_BUFFERED,
          "Could not calculate UART settings for target baud!");
      return;
    }
#endif /* ENABLE_ADVANCED_BAUD_SELECTION */

    /* Calculate Divisor register = 16MHz / (16 x baud rate) */
    u16Divisor = (uint16) (16000000UL / ((u8ClocksPerBit + 1) * baud_rate));

    /* Correct for rounding errors */
    u32Remainder =
        (uint32_t) (16000000UL % ((u8ClocksPerBit + 1) * baud_rate));

    if(u32Remainder >= (((u8ClocksPerBit + 1) * baud_rate) / 2)) {
      u16Divisor += 1;
    }

#if (ENABLE_ADVANCED_BAUD_SELECTION)
    DBG_vPrintf(DEBUG_UART_BUFFERED, "Divisor=%d, cpb=%d\n", u16Divisor,
        u8ClocksPerBit);

    u32CalcBaudRate = (16000000UL / ((u8ClocksPerBit + 1) * u16Divisor));

    DBG_vPrintf(DEBUG_UART_BUFFERED, "Calculated baud=%d\n", u32CalcBaudRate);

    i32BaudError = (int32) u32CalcBaudRate - (int32) baud_rate;

    DBG_vPrintf(DEBUG_UART_BUFFERED, "Error baud=%d\n", i32BaudError);
  }

  DBG_vPrintf(DEBUG_UART_BUFFERED, "Config uart=%d: Divisor=%d, cpb=%d\n",
      uart_dev, u16Divisor, u8ClocksPerBit);

  /* Set the calculated clocks per bit */
  vAHI_UartSetClocksPerBit(uart_dev, u8ClocksPerBit);
#endif /* ENABLE_ADVANCED_BAUD_SELECTION */

  /* Set the calculated divisor */
  vAHI_UartSetBaudDivisor(uart_dev, u16Divisor);
}

/****************************************************************************
 *
 * NAME:       uart_driver_isr
 *
 * DESCRIPTION:
 * Interrupt service callback for UART's
 *
 * PARAMETERS:        Name          RW  Usage
 *                    device_id	  	R   Device ID of whatever generated the
 *										                  interrupt
 *			          		item_bitmap	  R 	Which part of the device generated
 *									                	  the interrupt
 *
 * RETURNS:
 * void
 *
 ****************************************************************************/
static void
uart_driver_isr(uint32_t device_id, uint32_t item_bitmap)
{
  uint8_t uart_dev;
  switch(device_id) {
  case E_AHI_DEVICE_UART0:
    uart_dev = E_AHI_UART_0;
    break;
  case E_AHI_DEVICE_UART1:
    uart_dev = E_AHI_UART_1;
    break;
  default:
    return;
  }
  switch(item_bitmap) {
  /* byte available since a long time but RX-fifo not full: */
  case E_AHI_UART_INT_TIMEOUT:
  /* RX-fifo full: */
  case E_AHI_UART_INT_RXDATA:
    uart_driver_rx_handler(uart_dev);
    break;
  case E_AHI_UART_INT_TX:
#if UART_XONXOFF_FLOW_CTRL && UART_USE_TX_IRQ
    uart_driver_tx_handler(uart_dev);
#endif /* UART_XONXOFF_FLOW_CTRL */
    break;
  case E_AHI_UART_INT_RXLINE:
    /* rx-line interrupt is disabled. Should not get here */
    /* An error condition has occurred on the RxD line, such as
    a break indication, framing error, parity error or over-run. */
    break;
  }
}

/****************************************************************************
 *
 * NAME:       uart_driver_tx_in_progress
 *
 * DESCRIPTION:
 * Returns the state of data transmission
 *
 * PARAMETERS:      Name            RW  Usage
 *                  uart_dev        R   UART to use, eg, E_AHI_UART_0
 *
 * RETURNS:
 * uint8_t:   TRUE if data in buffer is being transmitted
 *            FALSE if all data in buffer has been transmitted by the UART
 *
 ****************************************************************************/
uint8_t
uart_driver_tx_in_progress(uint8_t uart_dev)
{

  if(u16AHI_UartReadTxFifoLevel(uart_dev) == 0) {
    if((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_TEMT ) != 0) {
      return FALSE;
    }
  }
  return TRUE;
}

#ifdef UART_EXTRAS

/****************************************************************************
 *
 * NAME:       uart_driver_flush
 *
 * DESCRIPTION:
 * Flushes the buffers of the specified UART
 *
 * PARAMETERS:      Name            RW  Usage
 *                  uart_dev        R   UART to disable, eg, E_AHI_UART_0
 *
 * RETURNS:
 * void
 *
 ****************************************************************************/
void
uart_driver_flush(uint8_t uart_dev)
{
  /* Disable TX Fifo empty and Rx data interrupts */
  uart_driver_disable_interrupts(uart_dev);

  /* flush hardware buffer */
  vAHI_UartReset(uart_dev, TRUE, TRUE);
  vAHI_UartReset(uart_dev, FALSE, FALSE);

  /* Re-enable TX Fifo empty and Rx data interrupts */
  uart_driver_enable_interrupts(uart_dev);
}
#endif /* UART_EXTRAS */
/****************************************************************************/
/***        END OF FILE                                                   ***/
/****************************************************************************/
